#include "zen_game_figure.h"
#include "zen_game_activity.h"

Zen::Game::FigureNode_::FigureNode_() {}

void Zen::Game::FigureNode_::SetVertexCount(size_t count)
{
	mVertexCount = count;
}

void Zen::Game::FigureNode_::SetVertexDrawRange(size_t drawOffset, size_t drawCount)
{
	mVertexDrawOffset = drawOffset;
	mVertexDrawCount = drawCount;
}

void Zen::Game::FigureNode_::SetPolygonBuffer(std::shared_ptr<Zen::Graphics::Buffer> buffer, size_t dimension)
{
	mPolygonBuffer = buffer;
	mPolygonDimension = dimension;
}

void Zen::Game::FigureNode_::SetIndexBuffer(std::shared_ptr<Zen::Graphics::IndexBuffer> buffer)
{
	mIndexBuffer = buffer;
}

std::shared_ptr<Zen::Graphics::Buffer> Zen::Game::FigureNode_::GetPolygonBuffer()
{
	return mPolygonBuffer;
}

std::shared_ptr<Zen::Graphics::IndexBuffer> Zen::Game::FigureNode_::GetIndexBuffer()
{
	return mIndexBuffer;
}

size_t Zen::Game::FigureNode_::GetPolygonDimension()
{
	return mPolygonDimension;
}

size_t Zen::Game::FigureNode_::GetVertexCount()
{
	return mVertexCount;
}

size_t Zen::Game::FigureNode_::GetVertexDrawOffset()
{
	return mVertexDrawOffset;
}

size_t Zen::Game::FigureNode_::GetVertexDrawCount()
{
	return mVertexDrawCount;
}

void Zen::Game::FigureNode_::Draw_(Zen::Graphics::Program* program, Zen::Graphics::EDrawMode mode, Zen::Graphics::EBlend blend, Zen::Graphics::EFace cull)
{
	if (mIndexBuffer)
	{
		auto offset = std::min(mVertexDrawOffset, mIndexBuffer->GetCount());
		auto count = std::min(mIndexBuffer->GetCount() - offset, mVertexDrawCount);
		program->DrawIndex(mode, mIndexBuffer.get(), offset, count, blend, cull);
	}
	else
	{
		auto offset = std::min(mVertexDrawOffset, mVertexCount);
		auto count = std::min(mVertexCount - offset, mVertexDrawCount);
		program->Draw(mode, mVertexDrawOffset, count, blend, cull);
	}
}

void Zen::Game::FigureBrief::OnReady(std::shared_ptr<RenderStack> stack, float)
{
	UpdateOverMatrix(stack->GetTopMatrix(), stack->GetTopMatrixVersion());
}

void Zen::Game::FigureBrief::OnDraw()
{
	auto program = mProgram ? mProgram : Activity::Only()->GetGraphicsEngine()->DefaultProgramUniformColor();
	program->Attach(mPolygonBuffer.get(), (int)mPolygonDimension, &GetMatrix(), &GetColor());
	this->Draw_(program.get(), mDrawMode, mBlend, mCulled);
}

void Zen::Game::FigureColors::SetColorBuffer(std::shared_ptr<Zen::Graphics::Buffer> buffer)
{
	mColorBuffer = buffer;
}

void Zen::Game::FigureColors::OnReady(std::shared_ptr<RenderStack> stack, float)
{
	UpdateOverMatrix(stack->GetTopMatrix(), stack->GetTopMatrixVersion());
}

void Zen::Game::FigureColors::OnDraw()
{
	auto program = mProgram ? mProgram : Activity::Only()->GetGraphicsEngine()->DefaultProgramVaryingColor();
	program->Attach(mPolygonBuffer.get(), (int)mPolygonDimension, &GetMatrix(), mColorBuffer.get(), &GetColor());

	this->Draw_(program.get(), mDrawMode, mBlend, mCulled);
}

void Zen::Game::FigureTexture::SetTexture(std::shared_ptr<Zen::Graphics::Texture> texture)
{
	auto frame = std::make_shared<TextureFrame>();
	frame->SetTexture(texture);
	mTextureFrame = frame;
}

void Zen::Game::FigureTexture::SetTextureFrame(std::shared_ptr<TextureFrameI> frame)
{
	if (mTextureFrame == frame) return;
	mTextureFrame = frame;
}

void Zen::Game::FigureTexture::SetTextureCoordBuffer(std::shared_ptr<Zen::Graphics::Buffer> buffer)
{
	mTextureCoordBuffer = buffer;
}

void Zen::Game::FigureTexture::OnTextureChange() {}

void Zen::Game::FigureTexture::OnReady(std::shared_ptr<RenderStack> stack, float)
{
	if (!mTextureFrame || !mTextureFrame->GetTexture()) return;
	if (!mPolygonBuffer || mPolygonDimension == 0 || mVertexCount == 0) return;
	if (!mTextureCoordBuffer) return;

	UpdateOverMatrix(stack->GetTopMatrix(), stack->GetTopMatrixVersion());
}

void Zen::Game::FigureTexture::OnDraw()
{
	if (!mTextureFrame || !mTextureFrame->GetTexture()) return;
	if (!mPolygonBuffer || mPolygonDimension == 0 || mVertexCount == 0) return;
	if (!mTextureCoordBuffer) return;

	auto e = Activity::Only()->GetGraphicsEngine();
	auto program = mProgram ? mProgram : (mTextureFrame->GetTexture()->GetPixelType() == EPixel::Grey ? e->DefaultProgramTextureA() : e->DefaultProgramTexture());
	program->Attach(mPolygonBuffer.get(), (int)mPolygonDimension, &GetMatrix(), mTextureCoordBuffer.get(), mTextureFrame->GetTexture().get(), &GetSampler(), &GetColor());

	this->Draw_(program.get(), mDrawMode, mBlend, mCulled);
}

std::shared_ptr<Zen::Game::TextureFrameI> Zen::Game::FigureTexture::GetTextureFrame()
{
	return mTextureFrame;
}

Zen::Game::FigurePointTexture::FigurePointTexture()
{
	mDrawMode = Zen::Graphics::EDrawMode::Point;
}

void Zen::Game::FigurePointTexture::SetTexture(std::shared_ptr<Zen::Graphics::Texture> texture)
{
	mTexture = texture;
}

void Zen::Game::FigurePointTexture::SetColorBuffer(std::shared_ptr<Zen::Graphics::Buffer> buffer)
{
	mColorBuffer = buffer;
}

void Zen::Game::FigurePointTexture::SetPointSizeBuffer(std::shared_ptr<Zen::Graphics::Buffer> buffer)
{
	mPointSizeBuffer = buffer;
}

void Zen::Game::FigurePointTexture::OnReady(std::shared_ptr<RenderStack> stack, float)
{
	if (!mTexture || !mPolygonBuffer || !mColorBuffer || !mPointSizeBuffer) return;

	UpdateOverMatrix(stack->GetTopMatrix(), stack->GetTopMatrixVersion());
}

void Zen::Game::FigurePointTexture::OnDraw()
{
	if (!mTexture || !mPolygonBuffer || !mColorBuffer || !mPointSizeBuffer) return;

	auto e = Activity::Only()->GetGraphicsEngine();
	auto program = mProgram ? mProgram : (mTexture->GetPixelType() == EPixel::Grey ? e->DefaultProgramPointTextureA() : e->DefaultProgramPointTexture());
	program->Attach(mPolygonBuffer.get(), 2, &GetMatrix(), mColorBuffer.get(), mPointSizeBuffer.get(), mTexture.get(), &GetSampler(), &GetColor());
	this->Draw_(program.get(), mDrawMode, mBlend, mCulled);
}
